home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / VBSamples / Demos / AirHockey / cPuck.cls < prev    next >
Encoding:
Visual Basic class definition  |  2001-10-08  |  17.7 KB  |  455 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "cPuck"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = True
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. Option Explicit
  15.  
  16. Private Const mnMaxSpinSpeed As Single = 0.9
  17. 'Here we will encapsulate all of the code needed for the puck
  18. 'Local variables for the properties of the puck
  19. Private moPosition As D3DVECTOR 'Current position of the puck
  20. Private moVelocity As D3DVECTOR 'Current velocity of the puck
  21. Private moLastPosition As D3DVECTOR 'Last position of the puck
  22.  
  23. Public Spinning As Boolean 'Is the puck currently spinning?
  24. Public MaximumPuckVelocity As Single
  25.  
  26. Private mnSpinDir As Single 'Direction of the pucks spinning
  27. Private mlPuckTime As Long 'Last time the puck was updated
  28. Private mnPuckSpin As Single
  29.  
  30. Private moPuck As CD3DFrame 'D3D Mesh for the puck
  31. 'Default spin speed
  32. Private mnDefaultSpin As Single
  33.  
  34. 'Position property
  35. Public Property Let Position(oPos As D3DVECTOR)
  36.     moPosition = oPos
  37. End Property
  38.  
  39. Public Property Get Position() As D3DVECTOR
  40.     Position = moPosition
  41. End Property
  42.  
  43. 'Velocity property
  44. Public Property Let Velocity(oVel As D3DVECTOR)
  45.     moVelocity = oVel
  46.     'Update the velocity, but make sure it isn't too high
  47.     EnsurePuckVelocityIsBelowMax
  48. End Property
  49.  
  50. Public Property Get Velocity() As D3DVECTOR
  51.     Velocity = moVelocity
  52. End Property
  53.  
  54. 'LastPosition prop
  55. Public Property Let LastPosition(oLastPos As D3DVECTOR)
  56.     moLastPosition = oLastPos
  57. End Property
  58.  
  59. Public Property Get LastPosition() As D3DVECTOR
  60.     LastPosition = moLastPosition
  61. End Property
  62.  
  63. 'Different methods from the puck.
  64. Public Sub Init(ByVal sMedia As String, sFile As String)
  65.     Set moPuck = D3DUtil_LoadFromFile(AddDirSep(sMedia) & sFile, Nothing, Nothing)
  66. End Sub
  67.  
  68. Public Sub UpdatePosition()
  69.     Dim RealVelocity As D3DVECTOR
  70.     
  71.     'Here we will update the position of the puck
  72.     'and move it based on the velocity assigned.
  73.     If mlPuckTime = 0 Then mlPuckTime = timeGetTime
  74.     'First calculate the 'real' velocity (based on the time)
  75.     RealVelocity.X = ((timeGetTime - mlPuckTime) / 100) * moVelocity.X
  76.     RealVelocity.Y = ((timeGetTime - mlPuckTime) / 100) * moVelocity.Y
  77.     RealVelocity.z = ((timeGetTime - mlPuckTime) / 100) * moVelocity.z
  78.     'Let's save our current position
  79.     moLastPosition = moPosition
  80.         
  81.     moPosition.X = moPosition.X + RealVelocity.X
  82.     moPosition.Y = moPosition.Y + RealVelocity.Y
  83.     moPosition.z = moPosition.z + RealVelocity.z
  84.     
  85.     If Spinning Then
  86.         'Update Puck Spin
  87.         mnPuckSpin = mnPuckSpin + ((((timeGetTime - mlPuckTime) / 100) * mnDefaultSpin) * mnSpinDir)
  88.         If mnPuckSpin > 2 * g_pi Then mnPuckSpin = 0
  89.     End If
  90.     
  91.     mlPuckTime = timeGetTime
  92. End Sub
  93.  
  94. Public Sub Render(dev As Direct3DDevice8)
  95.     Dim matRot As D3DMATRIX, matTrans As D3DMATRIX
  96.     Dim matPuck As D3DMATRIX
  97.     
  98.     D3DXMatrixRotationAxis matRot, vec3(0, 1, 0), mnPuckSpin
  99.     D3DXMatrixTranslation matTrans, moPosition.X, moPosition.Y, moPosition.z
  100.     D3DXMatrixMultiply matPuck, matRot, matTrans
  101.     
  102.     moPuck.SetMatrix matPuck
  103.     moPuck.Render dev
  104.  
  105. End Sub
  106.  
  107. Public Sub LaunchPuck()
  108.     Randomize
  109.     DefaultStartPosition
  110.     Do While (D3DXVec3Length(moVelocity) < (MaximumPuckVelocity / 4)) And (Abs(moVelocity.z) < 0.2) 'Make sure there is *some* z movement
  111.         moVelocity.z = Rnd * (MaximumPuckVelocity / 3)
  112.         moVelocity.X = Rnd * (MaximumPuckVelocity / 3)
  113.         If Rnd > 0.5 Then moVelocity.X = moVelocity.X * -1
  114.         If Rnd < 0.5 Then moVelocity.z = moVelocity.z * -1
  115.     Loop
  116. End Sub
  117.  
  118. Public Sub DefaultStartPosition()
  119.     moPosition = vec3(0, 2.5, 0)
  120.     moVelocity = vec3(0, 0, 0)
  121.     moLastPosition = vec3(0, 0, 0)
  122. End Sub
  123.  
  124. Public Sub ChangePuckVelocity(oPaddle As cPaddle, oAudio As cAudio, Optional ByVal fIgnoreMax As Boolean = False)
  125.  
  126.     Dim vDir As D3DVECTOR
  127.     Dim a As Single, b As Single, c As Single
  128.     Dim t0 As Single, t1 As Single
  129.     Dim vIntersect As D3DVECTOR, vIntersectHigh As D3DVECTOR
  130.     Dim oPlane As D3DPLANE, matReflect As D3DMATRIX
  131.     Dim oPoint As D3DVECTOR, vNewVelDir As D3DVECTOR
  132.     Dim vPuck As D3DVECTOR, tSmall As Single
  133.     Dim nVelocity As Single, nVelocityPaddle As Single
  134.     Dim vNewVelPad As D3DVECTOR
  135.   
  136.     'We hit with the paddle, randomly change the spin direction
  137.     UpdatePuckSpin
  138.     glPaddleCollideTime = timeGetTime
  139.     'gfRecentlyHitPaddle = True
  140.     'Notify the user that the puck hit the paddle by playing a sound
  141.     If Not (oAudio Is Nothing) Then oAudio.PlayHitSound
  142.     'Let's store the original velocity
  143.     nVelocity = D3DXVec3Length(moVelocity)
  144.     nVelocityPaddle = D3DXVec3Length(oPaddle.Velocity) * gnPaddleMass
  145.     'First we need to find the intersection point
  146.     'To do that we first need to solve for t:
  147.     'x = Dxt + x0
  148.     'z = Dzt + z0
  149.     D3DXVec3Subtract vPuck, moPosition, oPaddle.Position
  150.     D3DXVec3Normalize vDir, moVelocity
  151.     a = 1 ' (vDir.x ^ 2) + (vDir.z ^ 2) will always be one since the vector is normalized
  152.     b = (2 * vPuck.X * vDir.X) + (2 * vPuck.z * vDir.z)
  153.     c = ((vPuck.X ^ 2) + (vPuck.z ^ 2) - ((gnPaddleRadius + gnPuckRadius) ^ 2))
  154.     't = (-b â–’ SQR(bâ–“-4ac))/2a
  155.     If (b ^ 2) - (4 * a * c) > 0 Then
  156.         t0 = (-b + Sqr((b ^ 2) - (4 * a * c))) / (2 * a)
  157.         t1 = (-b - Sqr((b ^ 2) - (4 * a * c))) / (2 * a)
  158.     Else 'We shouldn't hit this case, but just in case.
  159.         t0 = 0
  160.         t1 = 0
  161.     End If
  162.     
  163.     Dim vInt1 As D3DVECTOR, vInt2 As D3DVECTOR
  164.     Dim vDifInt1 As D3DVECTOR, vDifInt2 As D3DVECTOR
  165.     'Find both possible intersection points
  166.     vInt1.X = (vDir.X * t0) + vPuck.X: vInt1.z = (vDir.z * t0) + vPuck.z
  167.     vInt2.X = (vDir.X * t1) + vPuck.X: vInt2.z = (vDir.z * t1) + vPuck.z
  168.     'Find the difference from the starting location
  169.     D3DXVec3Subtract vDifInt1, oPaddle.Position, vInt1
  170.     D3DXVec3Subtract vDifInt2, oPaddle.Position, vInt2
  171.         
  172.     'Find the smallest t
  173.     'If t0 > t1 Then
  174.     If D3DXVec3Length(vDifInt1) < D3DXVec3Length(vDifInt2) Then
  175.         tSmall = t1
  176.     Else
  177.         tSmall = t0
  178.     End If
  179.     'Let's get the intersected point
  180.     vIntersect.X = (vDir.X * tSmall) + vPuck.X
  181.     vIntersect.z = (vDir.z * tSmall) + vPuck.z
  182.     
  183.     'Create a new vector with an enormously high Y field to create our reflection plane
  184.     vIntersectHigh = vIntersect
  185.     vIntersectHigh.Y = 500
  186.     'Let's create a plane from this point
  187.     D3DXPlaneFromPoints oPlane, vec3(0, 0, 0), vIntersect, vIntersectHigh
  188.     
  189.     'Now we can create a reflection matrix based on this plane
  190.     D3DXMatrixReflect matReflect, oPlane
  191.     'Create a new point that is reflected
  192.     D3DXVec3TransformCoord oPoint, vPuck, matReflect
  193.     D3DXVec3Subtract vNewVelDir, oPoint, vIntersect
  194.     'Normalize the vector
  195.     D3DXVec3Normalize vNewVelDir, vNewVelDir
  196.     vNewVelDir.X = -vNewVelDir.X
  197.     vNewVelDir.z = -vNewVelDir.z
  198.     D3DXVec3Scale moVelocity, vNewVelDir, nVelocity
  199.     If nVelocityPaddle > 0 Then 'The paddle is moving, add it's velocity
  200.         'Now let's add the velocity of the paddle to our resulting velocity
  201.         D3DXVec3Normalize vNewVelPad, oPaddle.Velocity
  202.         D3DXVec3Scale vNewVelPad, vNewVelPad, nVelocityPaddle
  203.         D3DXVec3Add moVelocity, moVelocity, vNewVelPad
  204.     End If
  205.     Debug.Print "Old Velocity:"; nVelocity; "  - New Velocity:"; D3DXVec3Length(moVelocity)
  206.     'If we are limiting the velocity to it's maximum (most times), do so
  207.     If Not fIgnoreMax Then EnsurePuckVelocityIsBelowMax
  208. End Sub
  209.  
  210. Public Sub CheckCollisions(oPaddle() As cPaddle, Optional oAudio As cAudio = Nothing)
  211.     'First we should check to see if we are scoring in this frame.
  212.     Dim nDistance As Single
  213.     Dim lCount As Long, fCollided As Boolean
  214.     Dim lCollided As Long, nCollideDist As Single
  215.     
  216.     If gfScored Then Exit Sub
  217.     'Check to see if the puck has collided with any of the walls
  218.     'We could do an exhaustive check to see if any of the polygons collide, but since the table
  219.     'is static, in the name of faster calculations, we will use a group of constants defining the
  220.     'edges of the walls.  We will check those instead.
  221.         
  222.     'If the puck does hit one of the walls, we can easily calculate it's new direction by simply reversing
  223.     'it's velocity (of that vector).  If we want to be even more accurate we can lower the velocity by a small amount as well
  224.     
  225.     'The left and right walls are bound to the X axis
  226.     If moPosition.X > (gnSideLeftWallEdge - (gnPuckRadius)) Then
  227.         'We hit the wall
  228.         'Reverse the velocity of the X axis
  229.         moVelocity = vec3((moVelocity.X * -1) * gnVelocityDamp, 0, moVelocity.z)
  230.         moPosition = vec3((gnSideLeftWallEdge - (gnPuckRadius)), moPosition.Y, moPosition.z)
  231.         If Not (oAudio Is Nothing) Then oAudio.PlayBankSound
  232.         gfRecentlyHitPaddle = False
  233.     ElseIf moPosition.X < (gnSideRightWallEdge + (gnPuckRadius)) Then
  234.         'We hit the wall
  235.         moVelocity = vec3((moVelocity.X * -1) * gnVelocityDamp, 0, moVelocity.z)
  236.         moPosition = vec3((gnSideRightWallEdge + (gnPuckRadius)), moPosition.Y, moPosition.z)
  237.         If Not (oAudio Is Nothing) Then oAudio.PlayBankSound
  238.         gfRecentlyHitPaddle = False
  239.     End If
  240.     
  241.     'The front and rear walls are count to the Z axis
  242.     If moPosition.z > (gnNearWallEdge - (gnPuckRadius)) Then
  243.         'Only reverse the velocity if we hit the sides of the 'scoring area'
  244.         If (moPosition.X > (gnScoringEdgeLeft - (gnPuckRadius))) Or (moPosition.X < (gnScoringEdgeRight + (gnPuckRadius))) Then
  245.             'We hit the wall
  246.             'Reverse the velocity of the Z axis
  247.             moVelocity = vec3(moVelocity.X, 0, (moVelocity.z * -1) * gnVelocityDamp)
  248.             moPosition = vec3(moPosition.X, moPosition.Y, gnNearWallEdge - (gnPuckRadius))
  249.             If Not (oAudio Is Nothing) Then oAudio.PlayBankSound
  250.             gfRecentlyHitPaddle = False
  251.         End If
  252.     ElseIf moPosition.z < (gnFarWallEdge + (gnPuckRadius)) Then
  253.         If (moPosition.X > (gnScoringEdgeLeft - (gnPuckRadius))) Or (moPosition.X < (gnScoringEdgeRight - (gnPuckRadius))) Then
  254.             'We hit the wall
  255.             moVelocity = vec3(moVelocity.X, 0, (moVelocity.z * -1) * gnVelocityDamp)
  256.             moPosition = vec3(moPosition.X, moPosition.Y, gnFarWallEdge + (gnPuckRadius))
  257.             If Not (oAudio Is Nothing) Then oAudio.PlayBankSound
  258.             gfRecentlyHitPaddle = False
  259.         End If
  260.     End If
  261.     
  262.     'Next we should check to see if the puck has collided with either of the paddles
  263.     'We will use a simple formula to determine if the puck has collided with one of the
  264.     'paddles.  Simply put if the distance between the center of the puck, and the center
  265.     'of the paddle in question is greater than the radius of the puck + the radius of the
  266.     'paddle, they haven't collided
  267.     Dim vecDif As D3DVECTOR
  268.     
  269.     If ((timeGetTime - glPaddleCollideTime) > glMinDelayPaddleHit) Or (Not gfRecentlyHitPaddle) Then
  270.         gfRecentlyHitPaddle = False
  271.         For lCount = 0 To 1 'Both paddles
  272.             'We only check the X/Z coords because in this demo the puck will never leave the table
  273.             'so it will maintain a constant Y coord.
  274.             D3DXVec3Subtract vecDif, moPosition, oPaddle(lCount).Position
  275.             nDistance = D3DXVec3Length(vecDif)
  276.             If nDistance < (gnPaddleRadius + gnPuckRadius) Then 'They have collided
  277.                 nCollideDist = nDistance
  278.                 lCollided = lCount
  279.                 fCollided = True
  280.                 If gfMultiplayer Then
  281.                     'Let each client handle it's own collision detection
  282.                     'in a multiplayer game.  This balances the load between
  283.                     'the host machine, and the client machine and gives the
  284.                     'most realistic playing feel.
  285.                     If glMyPaddleID = lCount Then 'We collided with our paddle
  286.                         ChangePuckVelocity oPaddle(lCount), oAudio
  287.                         SendPuck
  288.                         SendCollidePaddle
  289.                     End If
  290.                 Else
  291.                     ChangePuckVelocity oPaddle(lCount), oAudio
  292.                 End If
  293.             End If
  294.         Next
  295.     End If
  296.     ' Make sure we aren't colliding anymore
  297.     If fCollided Then EnsurePuckIsNotInPaddle nCollideDist, oPaddle(lCollided)
  298.     'Lastly we should check if we have scored (on either side)
  299.     If gfMultiplayer And (Not gfHost) Then Exit Sub 'Only the host should check for scoring
  300.     If moPosition.z > (gnNearWallEdge) Then
  301.         'We scored!
  302.         goPuck.DropPuckIntoScoringPosition goAudio
  303.     ElseIf moPosition.z < (gnFarWallEdge) Then
  304.         'We scored!
  305.         goPuck.DropPuckIntoScoringPosition goAudio
  306.     End If
  307. End Sub
  308.  
  309. Public Sub EnsurePuckIsNotInPaddle(ByVal nDistance As Single, oPaddle As cPaddle, Optional ByVal fSentPaddle As Boolean = False)
  310.     'Move the paddle back out so it's not hitting the puck
  311.     Dim vDir As D3DVECTOR, vScale As D3DVECTOR, vPaddleVel As D3DVECTOR
  312.  
  313.     If fSentPaddle Then
  314.         D3DXVec3Subtract vPaddleVel, oPaddle.LastPosition, oPaddle.Position
  315.         'Get the direction vector by normalizing the paddle's velocity
  316.         D3DXVec3Normalize vDir, vPaddleVel
  317.     Else
  318.         'Get the direction vector by normalizing the pucks velocity
  319.         D3DXVec3Normalize vDir, moVelocity
  320.     End If
  321.     'Scale the vector, just enough to get it out of the paddle
  322.     D3DXVec3Scale vScale, vDir, (gnPuckRadius + gnPaddleRadius) - nDistance
  323.     'Move the puck to it's new location
  324.     D3DXVec3Add moPosition, moPosition, vScale
  325.     'Now, let's increase the pucks velocity that much as well..
  326.     If fSentPaddle Then D3DXVec3Add moVelocity, moVelocity, vScale
  327. End Sub
  328.  
  329. Private Sub UpdatePuckSpin()
  330.     Randomize
  331.     If Rnd > 0.5 Then
  332.         mnSpinDir = mnSpinDir * -1
  333.         'Update the spin, change speed from 75%-125% of current speed..
  334.         mnDefaultSpin = (Rnd * (mnSpinDir * 0.75)) + (mnSpinDir * 0.5)
  335.         If Abs(mnDefaultSpin) > mnMaxSpinSpeed Then
  336.             mnDefaultSpin = mnMaxSpinSpeed * (Abs(mnDefaultSpin) \ mnDefaultSpin)
  337.         End If
  338.     End If
  339. End Sub
  340.  
  341. Public Sub CleanupFrame()
  342.     moPuck.Destroy
  343.     Set moPuck = Nothing
  344. End Sub
  345.  
  346. Public Sub DropPuckIntoScoringPosition(oAudio As cAudio, Optional ByVal fFromReceive As Boolean = False)
  347.     
  348.     gfScored = True
  349.     glTimeCompPaddle = 0
  350.     If Not gfMultiplayer Then
  351.         With goPaddle(1).Velocity
  352.             .X = 0: .z = 0
  353.         End With
  354.     End If
  355.     glTimePuckScored = timeGetTime
  356.     oAudio.PlayScoreSound
  357.     If gfMultiplayer Then
  358.         If Not gfHost And Not fFromReceive Then Exit Sub
  359.     End If
  360.     'First stop the velocity
  361.     moVelocity = vec3(0, 0, 0)
  362.     
  363.     With moPosition
  364.         'Now position the puck
  365.         If .z < 0 Then
  366.             gPlayer(1).Score = gPlayer(1).Score + 1
  367.             .z = gnFarWallEdge - 1.2
  368.         ElseIf .z > 0 Then
  369.             .z = gnNearWallEdge + 1.2
  370.             gPlayer(0).Score = gPlayer(0).Score + 1
  371.         End If
  372.         If Abs(.X) > gnScoringEdgeLeft / 3 Then
  373.             If Abs(.X) <> .X Then
  374.                 .X = gnScoringEdgeRight / 3
  375.             Else
  376.                 .X = gnScoringEdgeLeft / 3
  377.             End If
  378.         End If
  379.         .Y = gnPuckScored
  380.     End With
  381.     
  382.     Spinning = False
  383.     'If we are the host, notify everyone that we've scored
  384.     If gfMultiplayer Then NotifyPlayersWeScored
  385.     
  386. End Sub
  387.  
  388. Public Function FadeMesh(FadeInterval As Single) As Boolean
  389.     Dim lNumMaterial As Long
  390.     Dim lCount As Long
  391.     Dim oMaterial As D3DMATERIAL8
  392.     Dim fDoneFading As Boolean
  393.     Dim oMesh As CD3DMesh
  394.     Dim nInternalInterval As Single
  395.     Static lFadeTime As Long
  396.     
  397.     nInternalInterval = FadeInterval
  398.     If lFadeTime = 0 Then
  399.         lFadeTime = timeGetTime
  400.         Exit Function 'We'll do the fade next render pass
  401.     End If
  402.     nInternalInterval = (((timeGetTime - lFadeTime) / 1000000) * nInternalInterval)
  403.     
  404.     Set oMesh = moPuck.FindChildObject("puck", 0)
  405.     fDoneFading = True
  406.     lNumMaterial = oMesh.GetMaterialCount
  407.     For lCount = 0 To lNumMaterial - 1
  408.         oMaterial = oMesh.GetMaterial(lCount)
  409.         If nInternalInterval > 0 And oMaterial.diffuse.a <= 1 Then
  410.             oMaterial.diffuse.a = oMaterial.diffuse.a + nInternalInterval
  411.             fDoneFading = False
  412.         ElseIf nInternalInterval < 0 And oMaterial.diffuse.a >= -1 Then
  413.             oMaterial.diffuse.a = oMaterial.diffuse.a + nInternalInterval
  414.             fDoneFading = False
  415.         End If
  416.         oMesh.SetMaterial lCount, oMaterial
  417.     Next
  418.     FadeMesh = fDoneFading
  419. End Function
  420.  
  421. Public Sub PauseSystem(ByVal fPause As Boolean)
  422.     If Not fPause Then
  423.         mlPuckTime = timeGetTime
  424.     End If
  425. End Sub
  426.  
  427. '************
  428. 'Private functions that the public subs here will call, but the main application doesn't need to know about
  429.  
  430. Private Sub EnsurePuckVelocityIsBelowMax()
  431.     Dim VelVec As D3DVECTOR
  432.     'Let's make sure the puck's velocity isn't above the max,
  433.     'and if it is, lower it to the max velocity
  434.     If D3DXVec3Length(moVelocity) > MaximumPuckVelocity Then
  435.         'Yup, lower the velocity to the max
  436.         Dim vNrm As D3DVECTOR
  437.         
  438.         D3DXVec3Normalize vNrm, moVelocity
  439.         D3DXVec3Scale VelVec, vNrm, MaximumPuckVelocity
  440.         moVelocity = VelVec
  441.     End If
  442. End Sub
  443.  
  444. Private Sub Class_Initialize()
  445.     mnSpinDir = 1
  446.     mnDefaultSpin = 0.15
  447.     Set moPuck = Nothing
  448.     DefaultStartPosition
  449. End Sub
  450.  
  451. Private Sub Class_Terminate()
  452.     If Not moPuck Is Nothing Then moPuck.Destroy
  453.     Set moPuck = Nothing
  454. End Sub
  455.